/*
 * Copyright (c) 2002-2003 by OpenSymphony
 * All rights reserved.
 */
package org.jackysoft.util;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.hibernate.util.DTDEntityResolver;
import org.jackysoft.opensymphony.workflow.entity.InvalidWorkflowDescriptorException;
import org.jackysoft.opensymphony.workflow.entity.WorkflowDescriptor;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;


/**
 * The WorkflowLoader is responsible for creating a WorkflowDesciptor
 * by loading the XML from various sources.
 *
 * @author <a href="mailto:plightbo@hotmail.com">Pat Lightbody</a>
 */
public class WorkflowLoader {
    //~ Methods ////////////////////////////////////////////////////////////////

    /**
     * @throws InvalidWorkflowDescriptorException 
     * @throws DocumentException 
     * @deprecated please use {@link #load(java.io.InputStream, boolean)} instead.
     */
    public static WorkflowDescriptor load(final InputStream is) throws DocumentException, InvalidWorkflowDescriptorException {
        return load(is, null, true);
    }

    public static WorkflowDescriptor load(final InputStream is, boolean validate) throws DocumentException, InvalidWorkflowDescriptorException{
        return load(is, null, validate);
    }

    /**
     * Load a workflow descriptor from a URL
     * @throws IOException 
     */
    public static WorkflowDescriptor load(final URL url, boolean validate) throws DocumentException, InvalidWorkflowDescriptorException, IOException {
        return load(url.openStream(), url, validate);
    }

    private static WorkflowDescriptor load(InputStream is, URL url, boolean validate) throws DocumentException, InvalidWorkflowDescriptorException {
    	SAXReader reader = new SAXReader();
        Document document =  reader.read(is);
    	document.setEntityResolver(new DTDEntityResolver());
    	document.setXMLEncoding("UTF-8");
        //new DTDEntityResolver()
        //new WorkflowErrorHandler(url)
        Element root = document.getRootElement();
        WorkflowDescriptor descriptor = new WorkflowDescriptor(root);
        if (validate) {
        	descriptor.validate();
        }
        return descriptor;
    }

    //~ Inner Classes //////////////////////////////////////////////////////////

    public static class AllExceptionsErrorHandler implements ErrorHandler {
        private final List exceptions = new ArrayList();

        public List getExceptions() {
            return exceptions;
        }

        public void error(SAXParseException exception) {
            addMessage(exception);
        }

        public void fatalError(SAXParseException exception) {
            addMessage(exception);
        }

        public void warning(SAXParseException exception) {
        }

        private void addMessage(SAXParseException exception) {
            exceptions.add(exception.getMessage() + " (line:" + exception.getLineNumber() + ((exception.getColumnNumber() > -1) ? (" col:" + exception.getColumnNumber()) : "") + ')');
        }
    }

    public static class WorkflowErrorHandler implements ErrorHandler {
        private URL url;

        public WorkflowErrorHandler(final URL url) {
            this.url = url;
        }

        public void error(SAXParseException exception) throws SAXException {
            throw new SAXException(getMessage(exception));
        }

        public void fatalError(SAXParseException exception) throws SAXException {
            throw new SAXException(getMessage(exception));
        }

        public void warning(SAXParseException exception) throws SAXException {
        }

        private String getMessage(SAXParseException exception) {
            return exception.getMessage() + " (" + ((url != null) ? (" url=" + url + ' ') : "") + "line:" + exception.getLineNumber() + ((exception.getColumnNumber() > -1) ? (" col:" + exception.getColumnNumber()) : "") + ')';
        }
    }
}
